package com.whfc.influx.dao.impl;

import com.whfc.common.enums.DelFlag;
import com.whfc.common.result.PageData;
import com.whfc.influx.dao.AppMachDataLogDao;
import com.whfc.mach.constant.MachMeasurement;
import com.whfc.mach.dto.AppMachDataLogDTO;
import com.whfc.mach.entity.AppMachDataLog;
import org.apache.commons.lang3.StringUtils;
import org.influxdb.InfluxDB;
import org.influxdb.dto.BatchPoints;
import org.influxdb.dto.Point;
import org.influxdb.dto.Query;
import org.influxdb.dto.QueryResult;
import org.influxdb.impl.InfluxDBMapper;
import org.influxdb.querybuilder.Select;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.util.ObjectUtils;

import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static org.influxdb.querybuilder.BuiltQuery.QueryBuilder.*;

/**
 * @Description:
 * @author: xugcheng
 * @version: 1.0
 * @date: 2020/7/1 10:08
 */
@Repository
public class AppMachDataLogDaoImpl implements AppMachDataLogDao {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 数据库名称
     */
    private static final String database = MachMeasurement.DATABASE;

    /**
     * 表名
     */
    private static final String measurement = MachMeasurement.MEASUREMENT;

    /**
     * 保留策略:保存10年数据
     */
    private static final String retentionPolicy = MachMeasurement.RETENTION_POLICY;

    /**
     * 时间单位:秒
     */
    private static final TimeUnit timeUnit = TimeUnit.SECONDS;

    @Autowired
    private InfluxDB influxDB;

    @Autowired
    private InfluxDBMapper influxDBMapper;

    @Override
    public void insert(AppMachDataLog record) {
        logger.debug("influxdb的insert方法");
        influxDB.setDatabase(database);
        influxDB.setRetentionPolicy(retentionPolicy);
        influxDB.write(this.buildDataPoint(record));
        //influxDBMapper.save(record);
    }

    @Override
    public void batchInsert(List<AppMachDataLog> recordList) {
        logger.debug("influxdb的batchInsert方法");
        BatchPoints.Builder batchBuiler = BatchPoints.builder();
        for (AppMachDataLog record : recordList) {
            Point point = this.buildDataPoint(record);
            batchBuiler.point(point);
        }
        influxDB.setDatabase(database);
        influxDB.setRetentionPolicy(retentionPolicy);
        influxDB.write(batchBuiler.build());
    }

    @Override
    public List<AppMachDataLogDTO> selectMachDataLogListByMachId(Integer machId, Date startTime, Date endTime) {
        logger.debug("influxdb的selectMachDataLogListByMachId方法");
        String[] columns = new String[]{"time", "workState", "netState", "oilPos", "pressure", "lng", "lat", "location"};
        Query query = select(columns).from(database, measurement)
                .where(eq("machId", String.valueOf(machId)))
                .and(eq("delFlag", DelFlag.UNDELETE.getValue()))
                .andNested()
                .and(gte("time", startTime.toInstant().toString()))
                .and(lte("time", endTime.toInstant().toString()))
                .close()
                .orderBy(asc());
        logger.debug(query.getCommand());
        List<AppMachDataLogDTO> list = influxDBMapper.query(query, AppMachDataLogDTO.class);
        this.transformTime(list);
        return list;
    }

    @Override
    public List<AppMachDataLogDTO> selectMachDataLogListByDeviceId(Integer deviceId, Date startTime, Date endTime) {
        logger.debug("influxdb的selectMachDataLogListByDeviceId方法");
        Query query = select().from(database, measurement)
                .where(eq("deviceId", String.valueOf(deviceId)))
                .and(eq("delFlag", DelFlag.UNDELETE.getValue()))
                .andNested()
                .and(gte("time", startTime.toInstant().toString()))
                .and(lte("time", endTime.toInstant().toString()))
                .close()
                .orderBy(desc());
        logger.debug(query.getCommand());
        List<AppMachDataLogDTO> list = influxDBMapper.query(query, AppMachDataLogDTO.class);
        this.transformTime(list);
        return list;
    }

    @Override
    public PageData<AppMachDataLogDTO> selectMachDataLogListByDeviceId(Integer deviceId, Date startTime, Date endTime, Integer pageNum, Integer pageSize) {
        logger.debug("influxdb的selectMachDataLogListByDeviceId方法");


        //统计总数量
        int total = 0;
        Query countQuery = select().count("workState")
                .from(database, measurement)
                .where(eq("deviceId", String.valueOf(deviceId)))
                .andNested()
                .and(gte("time", startTime.toInstant().toString()))
                .and(lte("time", endTime.toInstant().toString()))
                .close()
                .and(eq("delFlag", DelFlag.UNDELETE.getValue()));
        logger.info(countQuery.getCommand());
        QueryResult queryResult = influxDB.query(countQuery, TimeUnit.MILLISECONDS);
        List<QueryResult.Result> results = queryResult.getResults();
        if (results != null) {
            QueryResult.Result result = results.get(0);
            List<QueryResult.Series> seriesList = result.getSeries();
            if (seriesList != null && seriesList.size() > 0) {
                String numStr = seriesList.get(0).getValues().get(0).get(1).toString();
                total = Double.valueOf(numStr).intValue();
            }
        }
        logger.info("数量总数量total={}", total);

        //分页查询
        int pages = (total / pageSize) + ((total % pageSize) == 0 ? 0 : 1);
        int offset = (pageNum - 1) * pageSize;
        int limit = pageSize;
        List<AppMachDataLogDTO> list = Collections.EMPTY_LIST;
        if (pageNum <= pages) {
            Select select = select().from(database, measurement)
                    .where(eq("deviceId", String.valueOf(deviceId)))
                    .and(eq("delFlag", DelFlag.UNDELETE.getValue()))
                    .andNested()
                    .and(gte("time", startTime.toInstant().toString()))
                    .and(lte("time", endTime.toInstant().toString()))
                    .close()
                    .orderBy(desc());
            Query query = offset == 0 ? select.limit(limit) : select.limit(limit, offset);
            logger.debug(query.getCommand());
            list = influxDBMapper.query(query, AppMachDataLogDTO.class);
            this.transformTime(list);
        }

        PageData<AppMachDataLogDTO> pageData = new PageData<>();
        pageData.setList(list);
        pageData.setTotal((long) total);
        pageData.setPages(pages);
        pageData.setPageNum(pageNum);
        pageData.setPageSize(pageSize);
        return pageData;
    }

    /**
     * 构建数据点
     *
     * @param record
     * @return
     */
    private Point buildDataPoint(AppMachDataLog record) {
        logger.debug("influxdb的buildDataPoint方法");
        Point.Builder builder = Point
                .measurement(measurement)
                .time(record.getTime().toInstant().getEpochSecond(), timeUnit);
        if (!ObjectUtils.isEmpty(record.getDeviceId())) {
            builder.tag("deviceId", String.valueOf(record.getDeviceId()));
        }
        if (!ObjectUtils.isEmpty(record.getMachId())) {
            builder.tag("machId", String.valueOf(record.getMachId()));
        }
        if (!ObjectUtils.isEmpty(record.getWorkState())) {
            builder.addField("workState", record.getWorkState());
        }
        if (!ObjectUtils.isEmpty(record.getNetState())) {
            builder.addField("netState", record.getNetState());
        }
        if (!StringUtils.isEmpty(record.getLngFlag())) {
            builder.addField("lngFlag", record.getLngFlag());
        }
        if (!StringUtils.isEmpty(record.getLatFlag())) {
            builder.addField("latFlag", record.getLatFlag());
        }
        if (!ObjectUtils.isEmpty(record.getLng())) {
            builder.addField("lng", record.getLng());
        }
        if (!ObjectUtils.isEmpty(record.getLat())) {
            builder.addField("lat", record.getLat());
        }
        if (!ObjectUtils.isEmpty(record.getLngWgs84())) {
            builder.addField("lngWgs84", record.getLngWgs84());
        }
        if (!ObjectUtils.isEmpty(record.getLatWgs84())) {
            builder.addField("latWgs84", record.getLatWgs84());
        }
        if (!StringUtils.isEmpty(record.getLocation())) {
            builder.addField("location", record.getLocation());
        }
        if (!ObjectUtils.isEmpty(record.getRotationX())) {
            builder.addField("rotationX", record.getRotationX());
        }
        if (!ObjectUtils.isEmpty(record.getRotationY())) {
            builder.addField("rotationY", record.getRotationY());
        }
        if (!ObjectUtils.isEmpty(record.getRotationZ())) {
            builder.addField("rotationZ", record.getRotationZ());
        }
        if (!ObjectUtils.isEmpty(record.getBatteryState())) {
            builder.addField("batterState", record.getBatteryState());
        }
        if (!ObjectUtils.isEmpty(record.getBatteryPower())) {
            builder.addField("batterPower", record.getBatteryPower());
        }
        if (!ObjectUtils.isEmpty(record.getDeviceTemp())) {
            builder.addField("deviceTemp", record.getDeviceTemp());
        }
        if (!ObjectUtils.isEmpty(record.getPressure())) {
            builder.addField("pressure", record.getPressure());
        }
        if (!ObjectUtils.isEmpty(record.getOilPos())) {
            builder.addField("oilPos", record.getOilPos());
        }
        if (!ObjectUtils.isEmpty(record.getOilState())) {
            builder.addField("oilState", record.getOilState());
        }
        if (!ObjectUtils.isEmpty(record.getOilPower())) {
            builder.addField("oilPower", record.getOilPower());
        }
        if (!ObjectUtils.isEmpty(record.getOilTemp())) {
            builder.addField("oilTemp", record.getOilTemp());
        }
        if (!StringUtils.isEmpty(record.getMasterVer())) {
            builder.addField("masterVer", record.getMasterVer());
        }
        if (!StringUtils.isEmpty(record.getSlaveVer())) {
            builder.addField("slaveVer", record.getSlaveVer());
        }
        if (!ObjectUtils.isEmpty(record.getSpeed())) {
            builder.addField("speed", record.getSpeed());
        }
        if (!ObjectUtils.isEmpty(record.getElevation())) {
            builder.addField("elevation", record.getElevation());
        }
        if (!ObjectUtils.isEmpty(record.getDelFlag())) {
            builder.addField("delFlag", record.getDelFlag());
        }
        builder.addField("serverTime", System.currentTimeMillis() / 1000);
        return builder.build();
    }

    /**
     * 时间类型转换
     *
     * @param list
     */
    private void transformTime(List<AppMachDataLogDTO> list) {
        for (AppMachDataLogDTO dto : list) {
            dto.setTime(Date.from(dto.getDeviceTime()));
            if (!ObjectUtils.isEmpty(dto.getServerTime())) {
                dto.setCreateTime(new Date(dto.getServerTime() * 1000));
            }
        }
    }
}
